package com.see.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

import com.see.domain.Order;
import com.see.domain.Protocol;
import com.see.exception.NoSuchSocketConnectionException;
import com.see.exception.UserExistException;
import com.see.servise.SEEServise;

public class ServerCore {
	public static final int DEFAULT_PORT = 3000;

	private static ServerCore instance;
	public final int PORT;
	public boolean serverRunning = true;
	public ServerSocket serverSocket;
	public SEEServise seeServise;
	public Map<String, SocketConnection> connections = new HashMap<>();
	public ExecutorService pool;
	
	private static Logger logger = LoggerFactory.getLogger(ServerCore.class);
	
	public ServerCore(SEEServise seeServise){
		createServerCore(seeServise);
		this.PORT = ServerCore.DEFAULT_PORT;
	}

	public ServerCore(SEEServise seeServise, int port){
		createServerCore(seeServise);
		this.PORT = port;
	}
	
	private void createServerCore(SEEServise seeServise){
		this.seeServise = seeServise;
		this.instance = this;
		this.pool = Executors.newCachedThreadPool();
	}
	
	public void init() {
		pool.execute(new SocketListener(seeServise, this));
		logger.info("Listening on port " + PORT);
	}
	
	public void close(){
		try {
			logger.info("Shutting down...");
			this.serverRunning = false;
			pool.shutdown();
			for (SocketConnection connection : connections.values()) {
				connection.socket.close();
			}
			if (serverSocket != null) {
				serverSocket.close();
			}
			connections.clear();

			System.exit(0);
		} catch (IOException e) {
			logger.error("Error: " + e.getMessage());
		}

	}

	public static ServerCore getInstance() {
		return instance;
	}

	public synchronized void addConnection(String userName, SocketConnection socketConnection) throws UserExistException {
		SocketConnection tmpsocketconn = connections.get(userName);
		if (tmpsocketconn != null) {
			throw new UserExistException("UserExistException: user with name " + userName + " already exist");
		} else {
			connections.put(userName, socketConnection);
			socketConnection.setUserName(userName);
			sendOldFilledOrderToUser(userName, socketConnection);
			logger.info(userName + " register in user list");
			
		}
	}
	
	public synchronized SocketConnection getConnection(String userName) throws NoSuchSocketConnectionException{
		SocketConnection tmpsocketconn = connections.get(userName);
		if (tmpsocketconn == null) {
			throw new NoSuchSocketConnectionException();
		} 
		return tmpsocketconn;
	}
	
	public synchronized void removeConnection(String userName) throws NoSuchSocketConnectionException{
		SocketConnection tmpsocketconn = connections.get(userName);
		if (tmpsocketconn == null) {
			throw new NoSuchSocketConnectionException();
		}
		connections.remove(userName);
		logger.info(userName + " remove from user list");
	}
	
	public synchronized void sendMatchOrderToClient(Order order){
		
		String userName = order.getUser();
		try {
			SocketConnection socketConnection = getConnection(userName);
			socketConnection.placeMsgToOutputQueue(Protocol.server_sendorder(order));
		} catch (NoSuchSocketConnectionException e) {
			seeServise.addDeferredMatchOrdersToUser(userName, order);
		} 
	}
	
	public synchronized void sendOldFilledOrderToUser(String userName, SocketConnection socketConnection){
		ArrayList<Order> filledorders = seeServise.getDeferredMatchOrdersFromUser(userName);
		for (Order order : filledorders) {
			socketConnection.placeMsgToOutputQueue(Protocol
					.server_sendorder(order));
		}
	}

	
}
